#!/usr/bin/perl

use strict;
use Fcntl;
use Getopt::Std;
use POSIX ':sys_wait_h';

my $dvbstream = '/usr/local/bin/dvbstream';

my @programs = (
	'./rec.pl',
	'./epg.pl',
);

my %opts = ();
getopts('c:f:h:d:', \%opts);

my $card = $opts{'c'};
my $freq = $opts{'f'};
my $ch = $opts{'h'};
my $dir = $opts{'d'};

if ($card eq '' || $freq eq '' || $ch eq '' || $dir eq '') {
	die "Usage: $0 -c CARD -f FREQ -h CH -d DIR\n";
}

$SIG{CHLD} = \&sig_chld;

my @process = ();

foreach my $program (@programs) {
	push(@process, &execChild($program));
}

sub execChild {
	my($program) = @_;

	my $pid = open(my $handle, "| $program -h $ch -d $dir");

	my $proc = {
		program => $program,
		pid => $pid,
		handle => $handle,
	};

	return $proc;
}

my $lastrestart = 0;
my $restart = 0;

while(1) {

	my $packetsize = 1;
	my $error = 0;
	my $status = 0;

	my $pid;

eval {
	local $SIG{ALRM} = \&die_timeout;

	alarm(10);

	$pid = open(IN, "$dvbstream -c $card -f $freq 8192 -o |");

	while(read(IN, my $buf, $packetsize)) {
		alarm(5);

		if (1000 < $error) {
			&die_error;
		}

		my $pos = 0;
		my $sync_byte = vec($buf, $pos++, 8);

		if ($sync_byte == 0x47) {
			$packetsize = 188;

			while(length($buf) < 188 && read(IN, my $append, 1)) {
				$buf .= $append;
			}

			my $a = vec($buf, $pos++, 8);
			my $transport_error_indicator = $a >> 7 & 0x01;

			if ($transport_error_indicator) {
				$error++;
			}

			if ($status <= 0) {
				$0 = "TS [err:$error][res:$restart]";
				$status = 1000;
			}

			$status--;

			foreach my $proc (@process) {
				my $handle = $proc->{handle};
				print $handle $buf;
			}

		} else {
			$packetsize = 1;
			$error++;
		}
	}
};

	alarm(0);

	kill 15, $pid;
	close(IN);

	$restart++;
	$lastrestart = time();

	sleep(3);
}

sub die_timeout {
	if (time() < $lastrestart + 600) {
		alarm(10);
		return;
	}

	die "timeout stream\n";
}

sub die_error {
	if (time() < $lastrestart + 600) {
		return;
	}

	die "error 1000 count\n";
}

sub sig_chld {
	my $child = waitpid(-1, WNOHANG);

	for (my $i = 0; $i < @process; $i++) {
		my $proc = $process[$i];

		if ($child == $proc->{pid}) {
			close($proc->{handle});
			$process[$i] = &execChild($proc->{program});
		}
	}
}

